In [ ]:
epochs = 10

Teil 6 - Federated Learning auf MNIST mit einem CNN

Upgraden zu Federated Learning mit 10 Zeilen PyTorch + PySyft

Kontext

Federated Learning ist eine spannende und aufstrebende Machine Learning Technik, die es Systemen ermöglicht auf dezentralisierten Daten zu lernen. Der Grundgedanke dabei ist die Daten an ihrem ursprünglichen Ort zu belassen und dadurch die Privatsphäre des Besitzers zu stärken. Dafür wird jedoch das Machine Learning Model an alle Daten-Besitzer verteilt. Eine direkte Anwendung dafür ist die Vorhersage des nächsten Wortes beim Tippen auf dem Smartphone: die Trainingsdaten - z. B. die gesendeten Nachrichten - sollen hierbei keinesfalls auf einem zentralen Server gesammelt werden.

Die Verbreitung von Federated Learning ist stark an das Bewusstsein für Daten-Privatsphäre geknüpft. Die DSGVO der EU, welche seit Mai 2018 den Schutz der Daten vorschreibt, kann hierbei als Auslöser angesehen werden. Um einer Regulierung zuvorzukommen, haben große Unternehmen wie Apple oder Google begonnen, stark in diesen Bereich zu investieren und die Privatsphäre der eigenen Nutzer somit zu schützen. Jedoch stellen sie ihre Werkzeuge dafür nicht der Allgemeinheit zur Verfügung.
Bei OpenMined galuben wir daran, dass jeder sein Machine Learning Projekt einfach mit Werkzeugen zum Schutz der Privatsphäre ausstatten können sollte. Aus diesem Grund wurden z. B. Werkzeuge zum Verschlüsseln der Daten in einer einzigen Zeile von uns entworfen und nun veröffentlichen wir unser Federated Learning Gerüst, welches auf PyTorch 1.0 aufbaut, um ein intuitives Interface zum Bauen sicherer und skalierbarer Machine Learning Modele anzubieten.

In diesem Tutorial wird direkt mit dem Beispiel vom Trainieren eines CNN auf MNIST mit PyTorch gearbeitet. Es wird gezeigt wie einfach es ist dies mit der PySyft Bibliothek auf Federated Learning anzupassen. Dabei wird jeder Teil des Beispiels betrachtet und die angepassten Codezeilen hervorgehoben.

Das Material steht auch auf diesem Blogpost bereit.

Autoren:

Übersetzer:

Ok, lassen Sie uns starten!

Importe und Model Spezifikationen

Zuerst erfolgen die offiziellen Importe.


In [ ]:
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms

Darauf folgen die PySyft spezifischen Importe. Auch werden die Helfer alice und bob erstellt.


In [ ]:
import syft as sy  # <-- NEW: import the Pysyft library
hook = sy.TorchHook(torch)  # <-- NEW: hook PyTorch ie add extra functionalities to support Federated Learning
bob = sy.VirtualWorker(hook, id="bob")  # <-- NEW: define remote worker bob
alice = sy.VirtualWorker(hook, id="alice")  # <-- NEW: and alice

Die Einstellungen für das Lernen werden festgelegt.


In [ ]:
class Arguments():
    def __init__(self):
        self.batch_size = 64
        self.test_batch_size = 1000
        self.epochs = epochs
        self.lr = 0.01
        self.momentum = 0.5
        self.no_cuda = False
        self.seed = 1
        self.log_interval = 30
        self.save_model = False

args = Arguments()

use_cuda = not args.no_cuda and torch.cuda.is_available()

torch.manual_seed(args.seed)

device = torch.device("cuda" if use_cuda else "cpu")

kwargs = {'num_workers': 1, 'pin_memory': True} if use_cuda else {}

Laden der Daten und verteilen an die Helfer

Zuerst wird der Trainingsdatensatz vorverarbeitet und dann als Federated Datensatz auf alle Helfer mit der .federate Methode verteilt. Dieser gebündelte Datensatz wird einem Federated DataLoader übergeben. Der Test-Datensatz bleibt unangetastet.


In [ ]:
federated_train_loader = sy.FederatedDataLoader( # <-- this is now a FederatedDataLoader 
    datasets.MNIST('../data', train=True, download=True,
                   transform=transforms.Compose([
                       transforms.ToTensor(),
                       transforms.Normalize((0.1307,), (0.3081,))
                   ]))
    .federate((bob, alice)), # <-- NEW: we distribute the dataset across all the workers, it's now a FederatedDataset
    batch_size=args.batch_size, shuffle=True, **kwargs)

test_loader = torch.utils.data.DataLoader(
    datasets.MNIST('../data', train=False, transform=transforms.Compose([
                       transforms.ToTensor(),
                       transforms.Normalize((0.1307,), (0.3081,))
                   ])),
    batch_size=args.test_batch_size, shuffle=True, **kwargs)

CNN Spezifikationen

Hier wird exakt dasselbe CNN Model verwendet wie im offiziellen Beispiel.


In [ ]:
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(1, 20, 5, 1)
        self.conv2 = nn.Conv2d(20, 50, 5, 1)
        self.fc1 = nn.Linear(4*4*50, 500)
        self.fc2 = nn.Linear(500, 10)

    def forward(self, x):
        x = F.relu(self.conv1(x))
        x = F.max_pool2d(x, 2, 2)
        x = F.relu(self.conv2(x))
        x = F.max_pool2d(x, 2, 2)
        x = x.view(-1, 4*4*50)
        x = F.relu(self.fc1(x))
        x = self.fc2(x)
        return F.log_softmax(x, dim=1)

Definieren der Trainings und Test Funktionen

Weil die Trainings-Daten auf alice und bob aufgeteilt sind, muss in der Trainings-Funktion auch das Model für jeden Trainings-Batch an den richtigen Helfer versendet werden. Dann werden alle Operationen automatisiert aus der Ferne gestartet. Dabei wird dieselbe Syntax verwendet wie beim lokalen Verwenden von PyTorch. Anschließend wird das verbesserte Model zurück geholt und auch das Ergebnis der Loss-Funktion kann betrachtet werden.


In [ ]:
def train(args, model, device, federated_train_loader, optimizer, epoch):
    model.train()
    for batch_idx, (data, target) in enumerate(federated_train_loader): # <-- now it is a distributed dataset
        model.send(data.location) # <-- NEW: send the model to the right location
        data, target = data.to(device), target.to(device)
        optimizer.zero_grad()
        output = model(data)
        loss = F.nll_loss(output, target)
        loss.backward()
        optimizer.step()
        model.get() # <-- NEW: get the model back
        if batch_idx % args.log_interval == 0:
            loss = loss.get() # <-- NEW: get the loss back
            print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
                epoch, batch_idx * args.batch_size, len(federated_train_loader) * args.batch_size,
                100. * batch_idx / len(federated_train_loader), loss.item()))

Die Test-Funktion wird nicht abgeändert!


In [ ]:
def test(args, model, device, test_loader):
    model.eval()
    test_loss = 0
    correct = 0
    with torch.no_grad():
        for data, target in test_loader:
            data, target = data.to(device), target.to(device)
            output = model(data)
            test_loss += F.nll_loss(output, target, reduction='sum').item() # sum up batch loss
            pred = output.argmax(1, keepdim=True) # get the index of the max log-probability 
            correct += pred.eq(target.view_as(pred)).sum().item()

    test_loss /= len(test_loader.dataset)

    print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
        test_loss, correct, len(test_loader.dataset),
        100. * correct / len(test_loader.dataset)))

Starten des Trainings!


In [ ]:
%%time
model = Net().to(device)
optimizer = optim.SGD(model.parameters(), lr=args.lr) # TODO momentum is not supported at the moment

for epoch in range(1, args.epochs + 1):
    train(args, model, device, federated_train_loader, optimizer, epoch)
    test(args, model, device, test_loader)

if (args.save_model):
    torch.save(model.state_dict(), "mnist_cnn.pt")

Et voilà! Schon wurde ein Model mit Federated Learning auf fernen Daten trainiert!

Letzte Anmerkungen

Eine wichtige Frage bleibt zu klären: Wie lange dauert Federated Learning verglichen mit normalem PyTorch?

Die reine Rechenzeit dauert etwas weniger als doppelt so lange wie es mit normalem PyTorch dauern würde. Genau gemessen, dauert es 1.9 mal so lange, was im Hinblick auf die hinzugefügten Eigenschaften recht wenig ist.

PySyft auf GitHub einen Stern geben!

Der einfachste Weg, unserer Community zu helfen, besteht darin, die GitHub-Repos mit Sternen auszuzeichnen! Dies hilft, das Bewusstsein für die coolen Tools zu schärfen, die wir bauen.

Nutze unsere Tutorials auf GitHub!

Wir haben hilfreiche Tutorials erstellt, um ein Verständnis für Federated und Privacy-Preserving Learning zu entwickeln und zu zeigen wie wir die einzelnen Bausteine weiter entwickeln.

Mach mit bei Slack!

Der beste Weg, um über die neuesten Entwicklungen auf dem Laufenden zu bleiben, ist, sich unserer Community anzuschließen! Sie können dies tun, indem Sie das Formular unter http://slack.openmined.org ausfüllen.

Treten Sie einem Code-Projekt bei!

Der beste Weg, um zu unserer Community beizutragen, besteht darin, Entwickler zu werden! Sie können jederzeit zur PySyft GitHub Issues-Seite gehen und nach "Projects" filtern. Dies zeigt Ihnen alle Top-Level-Tickets und gibt einen Überblick darüber, an welchen Projekten Sie teilnehmen können! Wenn Sie nicht an einem Projekt teilnehmen möchten, aber ein wenig programmieren möchten, können Sie auch nach weiteren "einmaligen" Miniprojekten suchen, indem Sie nach GitHub-Problemen suchen, die als "good first issue" gekennzeichnet sind.

Spenden

Wenn Sie keine Zeit haben, zu unserer Codebase beizutragen, aber dennoch Unterstützung leisten möchten, können Sie auch Unterstützer unseres Open Collective werden. Alle Spenden fließen in unser Webhosting und andere Community-Ausgaben wie Hackathons und Meetups!


In [ ]: